//
//  XibView.swift
//  boleh
//
//  Created by BO ZHOU on 2020/9/8.
//  Copyright © 2020 BO ZHOU. All rights reserved.
//

import UIKit


/**
 * 原理:
 * 1.   如果直接创建一个Aview, 和Aview对应的Aview.xib, 并且绑定xib的view的class为Aview, 这种情况属于以前的正常开发情况,
 *      但是这种情况创建的Aview只能代码添加到别的控件上, 因为他需要通过Bundle.loadNibNamed 或者 UINib.instant去获取xib创建的东西,
 *      如果想要把Aview添加到另一个xib上, 是不可能的, 因为另一个xib去读取Aview实例的时候, 虽然会调用Aview.init(coder)方法, 但它并不知道Aview是通过xib创建的, 所以类似于调用了普通的init方法, xib上创建的subView完全不能被读取到
 *      如果想要解决上述问题,
 *      首先想到的就是在initWitCoder中使用 return UINib.instant或者Bundle.loadNibNamed方法, 但是这两个方法又会调用initWithCoder, 导致循环引用, 所以并不能解决
 *      解法见下面的2.
 *
 * 2.   通过自定义XibView解决上述问题方案:
 *      创建一个Bview, 创建一个名为Bview的xib, 修改xib的files owner为Bview (关于files owner看下面的附解)
 *      在Bview中创建一个contentView, 这个view专门用来指向从xib解析过来的UIView (xib的view的class不修改, 默认为UIView)
 *      这个view的解析代码可以写在Bview的initWithCoder中, 但是解析的并不是Bview本身, 而是Bview持有的一个view, 这样巧妙的避开了循环调用initWithCoder
 *      假设创建的xib的view的class不是UIView, 而是Bview, 那么在Bview的initWithCoder中要写相关从xib加载的方法, 不然父view是不能读取到xib中Bview的详情, 但是写了Bview的xib加载的方法又会循环调用Bview的initCoder导致无线循环调用
 *      但是我们通过将Bview想要的元素放在一个UIView中, 将这个UIView的xib的files owner修改为Bview, 在Bview的initCoder方法里获取这个UIView, 并且将这个UIView作为自己的content, 将这个UIView的frame修改为和自身一样大小, 巧妙的避开了循环引用的问题
 *
 *      而且通过@IBInspectable和@IBDesignable这两个修饰符, 可以动态的将Bview在initCoder的代码和xib设置的属性动态的渲染到其他xib上去
 *
 *
 *
 *
 * 3. 附解:
 *      files owner: files owner是标注这个xib属于谁的, 可以拖线到谁身上
 *      比如可以创建一个xib的view, 并且把files owner修改为某个控制器, 这样就可以把我们创建的xib的view拖线到控制器上
 *      同样, 我们创建一个Bview, 并且创建Bview.xib, 把Bview.xib的files owner修改为Bview, 意思是Bview持有这个xib中的view的控制权和拖线引用权
 */


@IBDesignable class XibView: UIView {
    
    var contentView: UIView!
    
    @IBInspectable var bgcolor: String {
        get {
            return ""
        } set {
            self.contentView.backgroundColor = UIColor.init(hexString: newValue)
        }
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        initXib()
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        initXib()
    }
    
    func initXib() {
        
        self.contentView = Self.loadNibView(instance: self)
        self.contentView.frame = bounds
        self.addSubview(contentView)
    }
}


extension UIViewProtocol where Self: XibView {
    
    static func loadNibView(instance: Any) -> UIView {
        
        // static方法里的self表示类self, className表示类对象
        let bundle = Bundle(for: self)
        let nib = UINib(nibName: "\(self)", bundle: bundle)
        return nib.instantiate(withOwner: instance, options: nil)[0] as! UIView
        
        // 如果以这种方式返回的话, xib会报错
        //        return Bundle.main.loadNibNamed("\(self)", owner: instance, options: nil)?.first as! UIView
    }
}
